package com.songlei.rtcadriondh265demo.rtc

import android.content.Context
import android.text.TextUtils
import android.util.Log
import com.google.gson.Gson
import com.songlei.rtcadriondh265demo.utils.BandwidthState
import com.songlei.rtcadriondh265demo.utils.CameraUtil
import com.songlei.rtcadriondh265demo.utils.WebRTCStats
import com.songlei.rtcadriondh265demo.utils.calculateBandwidth
import kotlinx.coroutines.*
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
import org.webrtc.*
import org.webrtc.audio.JavaAudioDeviceModule
import org.webrtc.voiceengine.WebRtcAudioUtils
import java.io.IOException
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
import kotlin.concurrent.schedule

/**
 *SDP 数据对像（bean）
 */
class SdpBean {
    var code = 0
    var sdp: String? = null
}


class WebRTCProxy(private val context: Context) : PeerConnection.Observer,
    SdpObserver {
    // 统计回调接口
    interface StatsCallback {
        fun onStatsUpdated(stats: WebRTCStats)
    }
    private val statsExecutor = Executors.newSingleThreadExecutor()
    private var statsJob: Job? = null

    private var eglBase: EglBase? = null
    private var playUrl: String? = null
    private var peerConnection: PeerConnection? = null
    private var surfaceViewRenderer: SurfaceViewRenderer? = null
    private var peerConnectionFactory: PeerConnectionFactory? = null
    private var audioSource: AudioSource? = null
    private var videoSource: VideoSource? = null
    private var localAudioTrack: AudioTrack? = null
    private var localVideoTrack: VideoTrack? = null
    private var captureAndroid: VideoCapturer? = null
    private var surfaceTextureHelper: SurfaceTextureHelper? = null
    private var isShowCamera = false
    private var isPublish = false //isPublish true为推流 false为拉流
    private var reConnCount = 0
    private var statsCallback: StatsCallback? = null

    // 初始化状态容器
    private val bandwidthState = BandwidthState()

    fun create(
        eglBase: EglBase?,
        surfaceViewRenderer: SurfaceViewRenderer?,
        isPublish: Boolean,
        isShowCamera: Boolean,
        playUrl: String?,
        callBack: WebRtcCallBack?,
        statsCallback: StatsCallback? = null
    ) {
        this.eglBase = eglBase
        this.surfaceViewRenderer = surfaceViewRenderer
        this.callBack = callBack
        this.playUrl = playUrl
        this.isPublish = isPublish
        this.isShowCamera = isShowCamera
        this.statsCallback = statsCallback
        init()
        startStatsCollection()

    }
    private fun startStatsCollection() {
        statsJob?.cancel()
        statsJob = null
        statsJob = CoroutineScope(Dispatchers.IO).launch {
            while (isActive) {
                peerConnection?.getStats { report ->
                    parseStatsReport(report)
                }
                delay(1000)
            }
        }
    }
    private fun RTCStats.getLong(key: String) = (members[key] as? Number)?.toLong() ?: 0
    private fun RTCStats.getInt(key: String) = (members[key] as? Number)?.toInt() ?: 0
    private fun RTCStats.getString(key: String) = members[key] as? String ?: "NA"
    private fun RTCStats.getDouble(key: String) = ((members[key] as? Number)?.toDouble() ?: 0.0)
    // 丢包率计算
    private fun calculateLossRatio(lost: Int, received: Int): Float {
        return when (val total = lost + received) {
            0 -> 0f
            else -> (lost.toFloat() / total) * 100
        }.coerceIn(0f..100f)
    }
    // 重传包比例
    private fun calculateRatio(part: Int, total: Int): Float {
        return if (total > 0) (part.toFloat() / total) * 100 else 0f
    }

    private fun parseStatsReport(report: RTCStatsReport) {
        val stats = WebRTCStats().apply {
            var totalBytes = 0L
            var timestampUs: Double? = null

            report.statsMap.values.forEach { stat ->
                when (stat.type) {
                    "outbound-rtp" -> {
                        width = stat.getInt("frameWidth")
                        height = stat.getInt("frameHeight")
                        fps = stat.getInt("framesPerSecond")
                        // 重传包比例
                        val retransmitted = stat.getInt("retransmittedPacketsSent")
                        val totalSent = stat.getInt("packetsSent")
                        retransmitRatio = calculateRatio(retransmitted, totalSent)
                        // NACK 计数
                        nackCount = stat.getInt("nackCount")

                        totalBytes += stat.getLong("bytesSent")
                        timestampUs = stat.timestampUs
                        val kind = stat.getString("kind")
                        val codecId = stat.getString("codecId")
                        codecId.let { id ->
                            val codecStat = report.statsMap[id]
                            if (codecStat?.type == "codec") {
                                val mimeType = codecStat.getString("mimeType")
                                when (kind) {
                                    "video" -> {
                                        videoMimeType = mimeType
                                    }
                                    "audio" -> {
                                        audioMimeType = mimeType
                                    }
                                }
                            }
                        }
                    }
                    "inbound-rtp" -> {
                        totalBytes += stat.getLong("bytesReceived")
                        timestampUs = stat.timestampUs

                        packetLoss = stat.getInt("packetsLost")
                        width = stat.getInt("frameWidth")
                        height = stat.getInt("frameHeight")
                        fps = stat.getInt("framesPerSecond")
                        jitter = ((stat.members["jitter"] as? Number)?.toDouble() ?: 0.0).times(1000).toInt()

                        val packetsLost = stat.getInt("packetsLost")
                        val packetsReceived = stat.getInt("packetsReceived")
                        packetLossRatio = calculateLossRatio(packetsLost, packetsReceived)

                        val kind = stat.getString("kind")
                        val codecId = stat.getString("codecId")
                        codecId.let { id ->
                            val codecStat = report.statsMap[id]
                            if (codecStat?.type == "codec") {
                                val mimeType = codecStat.getString("mimeType")
                                when (kind) {
                                    "video" -> {
                                        videoMimeType = mimeType
                                        videoPacketLoss = packetLossRatio
                                    }
                                    "audio" -> {
                                        audioMimeType = mimeType
                                        audioPacketLoss = packetLossRatio
                                    }
                                }
                            }
                        }

                    }
                    "candidate-pair" -> {
                        // currentRoundTripTime 是浮点数（单位：秒），转换为毫秒
                        val currentRoundTripTime = (stat.members["currentRoundTripTime"] as? Number)?.toDouble() ?: 0.0
                        rtt = (currentRoundTripTime * 1000).toInt()
                    }
                }
            }
            timestampUs?.let {
                calculateBandwidth(bandwidthState, totalBytes, it)?.let { bw->
                    bandwidth = bw
                }
            }

        }

        CoroutineScope(Dispatchers.Main).launch {
            statsCallback?.onStatsUpdated(stats)
        }
    }

    private fun init() {

        peerConnectionFactory = getPeerConnectionFactory(context)
        // 这可以通过使用 PeerConnectionFactory 类并调用 createPeerConnection() 方法来创建WebRTC PeerConnection
        Logging.enableLogToDebugOutput(Logging.Severity.LS_NONE)
        peerConnection = peerConnectionFactory!!.createPeerConnection(config, this)

        //拉流
        if (!isPublish) {
            peerConnection!!.addTransceiver(
                MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO,
                RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.RECV_ONLY)
            )
            peerConnection!!.addTransceiver(
                MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO,
                RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.RECV_ONLY)
            )
        }else {
            //推流
            peerConnection!!.addTransceiver(
                MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO,
                RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.SEND_ONLY)
            )
            peerConnection!!.addTransceiver(
                MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO,
                RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.SEND_ONLY)
            )

            //设置回声去噪
            WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true)
            WebRtcAudioUtils.setWebRtcBasedNoiseSuppressor(true)

            // 添加音频轨道
            audioSource = peerConnectionFactory!!.createAudioSource(createAudioConstraints())
            localAudioTrack = peerConnectionFactory!!.createAudioTrack(AUDIO_TRACK_ID, audioSource)
            localAudioTrack!!.setEnabled(true)
            peerConnection!!.addTrack(localAudioTrack)

            //添加视频轨道
            if (isShowCamera) {
                captureAndroid = CameraUtil.createVideoCapture(context)
                surfaceTextureHelper =
                    SurfaceTextureHelper.create("CameraThread", eglBase!!.eglBaseContext)
                videoSource = peerConnectionFactory!!.createVideoSource(false)
                captureAndroid!!.initialize(
                    surfaceTextureHelper,
                    context,
                    videoSource!!.capturerObserver
                )
                captureAndroid!!.startCapture(VIDEO_RESOLUTION_WIDTH, VIDEO_RESOLUTION_HEIGHT, FPS)
                localVideoTrack = peerConnectionFactory!!.createVideoTrack(VIDEO_TRACK_ID, videoSource)
                localVideoTrack!!.setEnabled(true)
                if (surfaceViewRenderer != null) {
                    val videoSink = ProxyVideoSink()
                    videoSink.setTarget(surfaceViewRenderer)
                    localVideoTrack!!.addSink(videoSink)
                }
                val videoSender =  peerConnection!!.addTrack(localVideoTrack)
                if (videoSender == null) {
                    Log.e(TAG, "Failed to add video track to PeerConnection")
                }
            }
        }
        peerConnection!!.createOffer(this, MediaConstraints())
    }

    /**
     * 获取 PeerConnectionFactory
     */
    private fun getPeerConnectionFactory(context: Context): PeerConnectionFactory {
       val initializationOptions: PeerConnectionFactory.InitializationOptions =
            PeerConnectionFactory.InitializationOptions.builder(context)
                .setEnableInternalTracer(true)
                .setFieldTrials("WebRTC-H264HighProfile/Enabled/;WebRTC-H265Enabled/Enabled/")
                .createInitializationOptions()
        PeerConnectionFactory.initialize(initializationOptions)


        // 2. 设置编解码方式
        //val encoderFactory: VideoEncoderFactory = SoftwareVideoEncoderFactory()
        val encoderFactory: VideoEncoderFactory = if (isPublish) {
            // 方法体内部新增 if 判断，根据 isPublish 选择不同的 VideoEncoderFactory
            PreferredVideoEncoderFactory(eglBase!!.eglBaseContext,
                true,
                true) // 推流时使用 PreferredVideoEncoderFactory
        } else {
            //默认方法
            DefaultVideoEncoderFactory(
                eglBase!!.eglBaseContext,
                false,
                true
            ) // 拉流时使用 DefaultVideoEncoderFactory
        }

        val decoderFactory: VideoDecoderFactory =
            DefaultVideoDecoderFactory(eglBase!!.eglBaseContext)

        // 构造Factory
        PeerConnectionFactory.initialize(
            PeerConnectionFactory.InitializationOptions
                .builder(context)
                .createInitializationOptions()
        )
        return PeerConnectionFactory.builder()
            .setOptions(PeerConnectionFactory.Options())
            .setAudioDeviceModule(JavaAudioDeviceModule.builder(context).createAudioDeviceModule())
            .setVideoEncoderFactory(encoderFactory)
            .setVideoDecoderFactory(decoderFactory)
            .createPeerConnectionFactory()
    }

    fun destroy() {
        statsJob?.cancel()
        statsExecutor.shutdown()

        if (callBack != null) {
            callBack = null
        }
        if (peerConnection != null) {
            peerConnection!!.dispose()
            peerConnection = null
        }
        if (surfaceTextureHelper != null) {
            surfaceTextureHelper!!.dispose()
            surfaceTextureHelper = null
        }
        if (captureAndroid != null) {
            captureAndroid!!.dispose()
            captureAndroid = null
        }
        if (surfaceViewRenderer != null) {
            surfaceViewRenderer!!.clearImage()
        }
        if (peerConnectionFactory != null) {
            peerConnectionFactory!!.dispose()
            peerConnectionFactory = null
        }
    }

    /**
     * 配置音频参数
     * @return
     */
    private fun createAudioConstraints(): MediaConstraints {
        val audioConstraints = MediaConstraints()
        audioConstraints.mandatory.add(
            MediaConstraints.KeyValuePair(AUDIO_ECHO_CANCELLATION_CONSTRAINT, "true")
        )
        audioConstraints.mandatory.add(
            MediaConstraints.KeyValuePair(AUDIO_AUTO_GAIN_CONTROL_CONSTRAINT, "false")
        )
        audioConstraints.mandatory.add(
            MediaConstraints.KeyValuePair(AUDIO_HIGH_PASS_FILTER_CONSTRAINT, "false")
        )
        audioConstraints.mandatory.add(
            MediaConstraints.KeyValuePair(AUDIO_NOISE_SUPPRESSION_CONSTRAINT, "true")
        )
        return audioConstraints
    }

    /**
     * 交换sdp
     */
    private fun getAnswer(sdp: String?) {
        reConnCount++
        val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
            override fun checkClientTrusted(chain: Array<out java.security.cert.X509Certificate>?, authType: String?) {}
            override fun checkServerTrusted(chain: Array<out java.security.cert.X509Certificate>?, authType: String?) {}
            override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> = emptyArray()
        })
        val sslContext = SSLContext.getInstance("SSL").apply {
            init(null, trustAllCerts, java.security.SecureRandom())
        }

        val client: OkHttpClient = OkHttpClient.Builder()
            .sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager)
            .connectTimeout(60, TimeUnit.SECONDS)
            .hostnameVerifier { _, _ -> true }
            .build()

        // 检查空值并处理
        val currentUrl = playUrl
        if (sdp == null || currentUrl == null) {
            Log.e("RequestError", "SDP or URL is null")
            return
        }

        val mediaType: MediaType = "application/json".toMediaTypeOrNull()
            ?: throw IllegalStateException("Failed to parse media type")
        val body = sdp.toRequestBody(mediaType)
        val request: Request = Request.Builder()
            .url(currentUrl)
            .method("POST", body)
            .addHeader("Content-Type", "application/json")
            .build()

        val call = client.newCall(request)
        call.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.d(TAG, "交换sdp reConnCount:$reConnCount  异常: ${e.message}")
                Timer().schedule(300) { //执行的任务
                    getAnswer(sdp)
                }
            }

            @Throws(IOException::class)
            override fun onResponse(call: Call, response: Response) {
                val result = response.body!!.string()
                Log.d(TAG, "交换sdp: $result")
                val sdpBean = Gson().fromJson(result, SdpBean::class.java)
                if (sdpBean != null && !TextUtils.isEmpty(sdpBean.sdp)) {
                    if (sdpBean.code == 400) {
                        Timer().schedule(300) { //执行的任务
                            getAnswer(sdp)
                        }
                    } else {
                        reConnCount = 0
                        setRemoteSdp(sdpBean.sdp)
                        if (callBack != null) callBack!!.onSuccess()
                    }
                }
            }
        })
    }

    fun setRemoteSdp(sdp: String?) {
        Log.d(TAG, "Answer SDP: $sdp")
        if (peerConnection != null) {
            val remoteSpd = SessionDescription(SessionDescription.Type.ANSWER, sdp)
            peerConnection!!.setRemoteDescription(this, remoteSpd)
        }
    }

    interface WebRtcCallBack {
        fun onSuccess()
        fun onFail()
    }
    private var callBack: WebRtcCallBack? = null

    //修改模式 PlanB无法使用仅接收音视频的配置
    private val config: PeerConnection.RTCConfiguration
         get() {
            val rtcConfig: PeerConnection.RTCConfiguration =
                PeerConnection.RTCConfiguration(ArrayList())
            //关闭分辨率变换
            rtcConfig.enableCpuOveruseDetection = false
            //修改模式 PlanB无法使用仅接收音视频的配置
            rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN
            return rtcConfig
        }

    override fun onCreateSuccess(sdp: SessionDescription) {
        Log.d(TAG, "Offer SDP (Video Check): ${sdp.description.contains("m=video")}")
        if (sdp.type === SessionDescription.Type.OFFER) {
            //设置setLocalDescription offer返回sdp
            Log.d(TAG, "Offer SDP: ${sdp.description}")
            peerConnection!!.setLocalDescription(this, sdp)
            if (!TextUtils.isEmpty(sdp.description)) {
                reConnCount = 0
                getAnswer(sdp.description)
            }
        }
    }


    override fun onSetSuccess() { }
    override fun onCreateFailure(error: String?) {}
    override fun onSetFailure(error: String?) {}
    override fun onSignalingChange(newState: PeerConnection.SignalingState?) {}
    override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState?) {}
    override fun onIceConnectionReceivingChange(receiving: Boolean) {}
    override fun onIceGatheringChange(newState: PeerConnection.IceGatheringState?) {}
    override fun onIceCandidate(candidate: IceCandidate?) {
        peerConnection!!.addIceCandidate(candidate)
    }
    override fun onIceCandidatesRemoved(candidates: Array<IceCandidate?>?) {
        peerConnection!!.removeIceCandidates(candidates)
    }
    override fun onAddStream(stream: MediaStream?) {}
    override fun onRemoveStream(stream: MediaStream?) {}
    override fun onDataChannel(dataChannel: DataChannel?) {}
    override fun onRenegotiationNeeded() {}
    override fun onAddTrack(receiver: RtpReceiver, mediaStreams: Array<MediaStream?>?) {
        val track: MediaStreamTrack = receiver.track()!!
        if (track is VideoTrack) {
            val remoteVideoTrack: VideoTrack = track
            remoteVideoTrack.setEnabled(true)
            if (surfaceViewRenderer != null && isShowCamera) {
                val videoSink = ProxyVideoSink()
                videoSink.setTarget(surfaceViewRenderer)
                remoteVideoTrack.addSink(videoSink)
            }
        }
    }

    companion object {
        private const val TAG = "WebRTCProxy"
        const val VIDEO_TRACK_ID = "video0"
        const val AUDIO_TRACK_ID = "audio0"
        private const val VIDEO_RESOLUTION_WIDTH = 1920
        private const val VIDEO_RESOLUTION_HEIGHT = 1080
        private const val FPS = 30
        private const val AUDIO_ECHO_CANCELLATION_CONSTRAINT = "googEchoCancellation"
        private const val AUDIO_AUTO_GAIN_CONTROL_CONSTRAINT = "googAutoGainControl"
        private const val AUDIO_HIGH_PASS_FILTER_CONSTRAINT = "googHighpassFilter"
        private const val AUDIO_NOISE_SUPPRESSION_CONSTRAINT = "googNoiseSuppression"
    }
}